/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package pe.com.trestperu.common.util;

import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.property.ChainedPropertyAccessor;
import org.hibernate.property.PropertyAccessor;
import org.hibernate.property.PropertyAccessorFactory;
import org.hibernate.property.Setter;
import org.hibernate.transform.ResultTransformer;

/**
 * 
 *
 * @author Telly Tataje
 *
 */
public class AliasToBeanResultTransformer implements ResultTransformer {
	
	private static final Log log = LogFactory.getLog(AliasToBeanResultTransformer.class);

	private static final long serialVersionUID = 1L;
	private final Class<?> resultClass;
    private Setter setters[];
    private PropertyAccessor propertyAccessor;

	public AliasToBeanResultTransformer(Class<?> resultClass) {
		if (resultClass == null) {
			throw new IllegalArgumentException("resultClass cannot be null");
		} else {
			this.resultClass = resultClass;
			propertyAccessor = new ChainedPropertyAccessor(new PropertyAccessor[] {
					PropertyAccessorFactory.getPropertyAccessor(resultClass, null), PropertyAccessorFactory.getPropertyAccessor("field")
			});
			return;
		}
	}

	@SuppressWarnings("unchecked")
	public List transformList(List collection) {
		return collection;
	}

	public Object transformTuple(Object tuple[], String aliases[]) {
        Object result;
        try {
        	if (setters == null) {
        		setters = new Setter[aliases.length];
        		for (int i = 0; i < aliases.length; i++) {
        			String alias = aliases[i];
        			if (alias != null) {
        				try{
        					setters[i] = propertyAccessor.getSetter(resultClass, convertToProperty(resultClass, alias));
        				}catch(Exception e){
        					setters[i] = null;
        					log.error(e);
        				}
        			}
        		}

        	}
        	result = resultClass.newInstance();
        	for (int i = 0; i < aliases.length; i++) {
        		if (setters[i] != null) {
        			try{
        				setters[i].set(result, tuple[i], null);
        			}catch(Exception e){
        				log.error(e);
        				try{
        					String property = setters[i].getMethodName().substring(3);
        					property = String.valueOf(property.charAt(0)).toLowerCase()+property.substring(1);
        					BeanUtils.setProperty(result, property, tuple[i]);
        				}catch(Exception ex){
        					log.warn(ex);
        				}
        			}
        		}
        	}

        }catch (InstantiationException e) {
        	throw new HibernateException("Could not instantiate resultclass : " + resultClass.getName());
        }
        catch (IllegalAccessException e) {
        	throw new HibernateException("Could not instantiate resultclass : " + resultClass.getName());
        }
        return result;
	}

	private String convertToProperty(Class<?> bean, String property){
		String result = StringUtils.EMPTY;
		Map<String, Object> fields = ReflectionUtils.discoverFields(bean);
		Iterator<?> it = fields.keySet().iterator();
		while(it.hasNext()){
			Object key = it.next();
			Field field = (Field)fields.get(key);
			if(property.equalsIgnoreCase(field.getName())){
				result = field.getName();
				break;
			}
		}
		return result;
	}
}